/*
 * Panograph.cpp
 *
 *  Created on: 02/09/2011
 *      Author: solnox
 */

#include "Panograph.h"
#include "ImageAnalyzer.h"
#include "ImageContainer.h"
#include <cmath>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
using namespace std;
using namespace cv;

#define WRITE_MATCHES 0
#define TRANSITION_SMOOTH 0
#define EXPCOR 0
#define FIND_BESTMATCH 0



Panograph::Panograph(string input_path) {

	input_path_ = input_path;


	int status;
	if(WRITE_MATCHES)
		status = mkdir(string(input_path_ + "/matches").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
	if(!TRANSITION_SMOOTH && !EXPCOR)
		status = mkdir(string(input_path_ + "/pano_averaged_no_expcor").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
	if(!TRANSITION_SMOOTH && EXPCOR)
		status = mkdir(string(input_path_ + "/pano_averaged_expcor").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
	if(TRANSITION_SMOOTH && !EXPCOR)
		status = mkdir(string(input_path_ + "/pano_smooth_no_expcor").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
	if(TRANSITION_SMOOTH && EXPCOR)
		status = mkdir(string(input_path_ + "/pano_smooth_expcor").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

	min_matches_ = 20;
	number_of_images_in_panograph_ = 0;

}

Panograph::~Panograph() {
}


void Panograph::buildPanograph(std::vector<cv::Mat*> images)
{

	for(int i = 0; i < (int)images.size(); i++)
	{
		cv::Mat * image_ptr = images.at(i);
		if(image_ptr == NULL)
		{

			cout << "Image pointer is invalid! --> ABORTING!" << endl;
			return;
		}
		AnalyzedImage analyzed_img(image_ptr);
		images2add_.push_back(analyzed_img);
	}

	if(images2add_.size() == 0)
	{

		cout << "No images!" << endl;
		return;
	}
	else
		cout << "All images analysed!" << endl;

	panograph_ = images2add_.at(0);
	number_of_images_in_panograph_++;
	images2add_.erase (images2add_.begin());

	while(!images2add_.empty())
	{
		int best_match_ind = 0;
		int best_match_size = 0;
		vector<DMatch> found_matches;
		if(FIND_BESTMATCH)
		{

			vector<vector<DMatch> > match_vec;
			for(int i= 0; i < (int)images2add_.size(); i++)
			{
				vector<DMatch> matches;
				ImageAnalyzer::getInstance().match(panograph_,images2add_.at(i),matches);
				match_vec.push_back(matches);
				vector<Point2f> matched_pts1;
				vector<Point2f> matched_pts2;


				matches2points(images2add_.at(i).keypoints ,panograph_.keypoints,matches , matched_pts2, matched_pts1);

				vector<unsigned char> match_mask;
				Mat H = findHomography(matched_pts1, matched_pts2, CV_RANSAC, 3, match_mask);

				int correct_matches = 0;
				for(int j = 0; j < (int)match_mask.size() ; j++)
				{
					if(match_mask.at(j) != 0)
						correct_matches++;
				}
				if(correct_matches > best_match_size)
				{
					best_match_size = correct_matches;
					best_match_ind = i;
				}
			}
			if((int)best_match_size < min_matches_)
			{
				cout << "best_match_size was " << best_match_size << "! No good images left!" << endl;
				break;
			}

			found_matches = match_vec.at(best_match_ind);
		}
		else
		{
			ImageAnalyzer::getInstance().match(panograph_,images2add_.at(0),found_matches);
		}


		// integrate the best match!
		AnalyzedImage best_img = images2add_.at(best_match_ind);
		images2add_.erase(images2add_.begin()+best_match_ind);
		vector<Point2f> matched_pts1;
		vector<Point2f> matched_pts2;


		matches2points(best_img.keypoints ,panograph_.keypoints,found_matches , matched_pts2, matched_pts1);

		vector<unsigned char> match_mask;
		Mat H = findHomography(matched_pts1, matched_pts2, CV_RANSAC, 3, match_mask);

		int correct_matches = 0;
		for(int i = 0; i < (int)match_mask.size() ; i++)
		{
			if(match_mask.at(i) != 0)
				correct_matches++;
		}
		cout << "number of consistent matches: "  << correct_matches << endl;

		if(WRITE_MATCHES)
		{
			Mat output_img;
			vector<char> signed_mask( match_mask.begin(), match_mask.end() );
			drawMatches(panograph_.rgb_image, panograph_.keypoints, best_img.rgb_image,  best_img.keypoints, found_matches, output_img,Scalar::all(-1),Scalar::all(-1),signed_mask);
			vector<int> params;
			params.push_back(CV_IMWRITE_JPEG_QUALITY);
			params.push_back(100);
			imwrite(string(input_path_ + "/matches/" + int2string(number_of_images_in_panograph_) +".jpg"), output_img,params);
		}

		vector<Vec2d> corners;
		corners.push_back(Vec2d(0,0));
		corners.push_back(Vec2d(best_img.rgb_image.cols-1,0));
		corners.push_back(Vec2d(0,best_img.rgb_image.rows-1));
		corners.push_back(Vec2d(best_img.rgb_image.cols-1,best_img.rgb_image.rows-1));

		vector<Vec2d> trafo_corners(4);
		perspectiveTransform(corners, trafo_corners, H.inv());

		Vec2d min_vec, max_vec;
		getMinMaxVectors(trafo_corners,  min_vec, max_vec);

		Vec2i shift,padding;
		if(min_vec[1] < 0)
			shift[1] = ceil(fabs(min_vec[1]));
		else
			shift[1] = 0;

		if(min_vec[0] < 0)
			shift[0] = ceil(fabs(min_vec[0]));
		else
			shift[0] = 0;

		if(max_vec[1] >= panograph_.bw_image.rows)
			padding[1] = ceil(max_vec[1]) -panograph_.bw_image.rows -1;
		else
			padding[1]= 0;

		if(max_vec[0] >= panograph_.bw_image.cols)
			padding[0] = ceil(max_vec[0]) -panograph_.bw_image.cols -1;
		else
			padding[0]= 0;

		Size out_size(shift[0]+padding[0]+panograph_.bw_image.cols,shift[1]+padding[1]+panograph_.bw_image.rows);

		Mat out_img = Mat::zeros(out_size,CV_8UC3);
		Mat big_warped_img = Mat::zeros(out_size,CV_8UC3);
		Mat shifted_pano = Mat::zeros(out_size,CV_8UC3);

		Mat pano_part = shifted_pano(Range(shift[1],shift[1]+panograph_.bw_image.rows),Range(shift[0],shift[0]+panograph_.bw_image.cols));
		panograph_.rgb_image.copyTo(pano_part);

		Mat first_part = out_img(Range(shift[1],shift[1]+panograph_.bw_image.rows),Range(shift[0],shift[0]+panograph_.bw_image.cols));
		panograph_.rgb_image.copyTo(first_part);


		Mat big_pano_mask = Mat::zeros(out_size,CV_8UC1);
		Mat pano_mask_copy = big_pano_mask(Range(shift[1],shift[1]+panograph_.bw_image.rows),Range(shift[0],shift[0]+panograph_.bw_image.cols));
		panograph_.mask.copyTo(pano_mask_copy);

		Mat big_img_mask = Mat::zeros(out_size,CV_8UC1);


		Mat T = Mat::eye(Size(3,3),CV_64FC1);
		T.at<double>(0,2) = -shift[0];
		T.at<double>(1,2) = -shift[1];
		Mat shifted_H =  H * T ; // care opencv multiplies the wrong way!!!

		warpPerspective(best_img.rgb_image, big_warped_img, shifted_H.inv(), out_size, INTER_CUBIC,BORDER_TRANSPARENT);
		warpPerspective(best_img.mask, big_img_mask, shifted_H.inv(), out_size, INTER_CUBIC,BORDER_TRANSPARENT);



		// overlap handling
		Mat overlap_mask = big_pano_mask.mul(big_img_mask);

		if(EXPCOR)
		{
			double sum_pano[3];
			double sum_img[3];
			for(int c= 0; c < 3 ; c++)
			{
				sum_pano[c] = 0;
				sum_img[c] = 0;
			}
			for(int y = 0 ; y < overlap_mask.rows ; y++)
				for(int x = 0; x < overlap_mask.cols; x++)
				{
					if(overlap_mask.at<char>(y,x) == 1)
					{
						for(int c= 0; c < 3 ; c++)
						{
							sum_pano[c] += (unsigned char)shifted_pano.at<cv::Vec3b>(y,x)[c];
							sum_img[c] += (unsigned char)big_warped_img.at<cv::Vec3b>(y,x)[c];
						}
					}
				}

			int exposure_correction = (int)round((((sum_pano[0]/sum_img[0])-1) +((sum_pano[1]/sum_img[1])-1) +((sum_pano[2]/sum_img[2])-1)) *255/6);

			cout << "exposure_correction " <<  exposure_correction << endl;
			for(int y = 0 ; y < overlap_mask.rows ; y++)
				for(int x = 0; x < overlap_mask.cols; x++)
				{
					if(big_img_mask.at<char>(y,x) == 1)
					{
						for(int c= 0; c < 3 ; c++)
						{
							big_warped_img.at<cv::Vec3b>(y,x)[c] = (unsigned char)max(min((int)big_warped_img.at<cv::Vec3b>(y,x)[c] + exposure_correction,255),0);
						}
					}
				}
		}

		//calculate a transition weighting
		if(TRANSITION_SMOOTH)
			stitchSmoothly(big_img_mask, big_pano_mask,  overlap_mask,  big_warped_img,  shifted_pano,  out_img);
		else
			stitchAveraging(big_img_mask, big_pano_mask,  overlap_mask,  big_warped_img,  shifted_pano,  out_img);




		for(int y = 0 ; y < overlap_mask.rows ; y++)
			for(int x = 0; x < overlap_mask.cols; x++)
			{
				if(big_img_mask.at<char>(y,x) == 1 )
					big_pano_mask.at<char>(y,x) = 1;
			}

		panograph_.mask = big_pano_mask;


		panograph_.rgb_image = out_img;

		cvtColor(panograph_.rgb_image, panograph_.bw_image, CV_RGB2GRAY);

		string foldername;
		if(TRANSITION_SMOOTH)
			foldername = "pano_smooth_";
		else
			foldername = "pano_averaged_";

		if(EXPCOR)
			foldername = string(foldername+"expcor");
		else
			foldername = string(foldername+"no_expcor");

		vector<int> params;
		params.push_back(CV_IMWRITE_JPEG_QUALITY);
		params.push_back(100);
		imwrite(string(input_path_ + "/"+foldername+"/" + int2string(number_of_images_in_panograph_) +".jpg"), out_img,params);


		//update key points and descriptors like wise
		shiftKeyPoints(panograph_.keypoints, shift);
		vector<KeyPoint> warped_kps;
		warpKeyPoints(best_img.keypoints, warped_kps,  shifted_H);
		panograph_.keypoints.insert(panograph_.keypoints.end(), warped_kps.begin(), warped_kps.end());
		panograph_.desc.push_back(best_img.desc);

		cout << "rows: " <<  panograph_.desc.rows << endl;
		cout << "cols: " <<  panograph_.desc.cols<< endl;
		cout << "Added Image!" << endl;

		number_of_images_in_panograph_++;

	}


}


void Panograph::benchmarkPanograph(string benchmark_path)
{

	vector<string> considered_folders;

	//considered_folders.push_back(string("rotation"));
	//considered_folders.push_back(string("scaling_bonus"));
	//considered_folders.push_back(string("illumination"));
	//considered_folders.push_back(string("perspective"));
	considered_folders.push_back(string("gaussian_blur_bonus"));
	//considered_folders.push_back(string("snp_noise"));
	//considered_folders.push_back(string("gaussian_noise"));

	cv::Mat base = cv::imread(string(benchmark_path+"/base.jpg"));
	if(base.empty())
	{
		cerr << "base image not found! benchmark aborting!" << endl;
		return;
	}
	cv::Mat * base_img_ptr = new cv::Mat(base); //will produce memory leak

	AnalyzedImage base_img(base_img_ptr);
	if(true)
	{

		vector<DMatch> found_matches;
		ImageAnalyzer::getInstance().match(base_img,base_img,found_matches);
		unsigned int correct_matches = found_matches.size();
		ofstream out(string(benchmark_path +"/base.txt").c_str());
		if(!out) {
			cerr << "Cannot open file" << string(benchmark_path +"/base.txt") << ".\n";
			return;
		}

		out << correct_matches << endl;
		out.close();

		cout << "max number of matches: "  << correct_matches << endl;
	}

	for(int folder_ind = 0; folder_ind < (int) considered_folders.size();folder_ind++)
	{
		string current_folder = considered_folders.at(folder_ind);
		string current_path = string(benchmark_path+"/"+current_folder);
		int status = mkdir(string(current_path+"/analysis").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

		ImageContainer ic;
		ic.loadImages(current_path,".jpg");
		std::vector<cv::Mat*> images = ic.getAllImages();
		vector<string> file_names = ic.getAllImageNames();
		for(int img_ind = 0; img_ind < (int)images.size();img_ind++)
		{
			cv::Mat * image_ptr = images.at(img_ind);
			if(image_ptr == NULL)
			{

				cout << "Image pointer is invalid! --> ABORTING!" << endl;
				return;
			}
			string current_img_path = file_names.at(img_ind);
			int found = current_img_path.find_last_of("/");
			current_img_path = current_img_path.insert( found+1 , "analysis/" );

			AnalyzedImage analyzed_img(image_ptr);
			vector<DMatch> found_matches;
			ImageAnalyzer::getInstance().match(base_img,analyzed_img,found_matches);

			if(found_matches.size() < 4)
			{
				ofstream out(string(current_img_path +"_matches.txt").c_str());
				if(!out) {
					cerr << "Cannot open file" << string(current_img_path +"_matches.txt") << ".\n";
					return;
				}

				out << "fail" << endl;
				out.close();

				cout << "number of consistent matches: "  << "fail" << endl;
				continue;

			}
			vector<Point2f> matched_pts1;
			vector<Point2f> matched_pts2;


			matches2points(analyzed_img.keypoints ,base_img.keypoints,found_matches , matched_pts2, matched_pts1);

			vector<unsigned char> match_mask;
			Mat H = findHomography(matched_pts1, matched_pts2, CV_RANSAC, 3, match_mask);

			int correct_matches = 0;
			for(int i = 0; i < (int)match_mask.size() ; i++)
			{
				if(match_mask.at(i) != 0)
					correct_matches++;
			}

			ofstream out(string(current_img_path +"_matches.txt").c_str());
			if(!out) {
				cerr << "Cannot open file" << string(current_img_path +"_matches.txt") << ".\n";
				return;
			}

			out << correct_matches << endl;
			out.close();

			cout << "number of consistent matches: "  << correct_matches << endl;


			if(WRITE_MATCHES)
			{
				Mat output_img;
				vector<char> signed_mask( match_mask.begin(), match_mask.end() );
				drawMatches(base_img.rgb_image, base_img.keypoints, analyzed_img.rgb_image,  analyzed_img.keypoints, found_matches, output_img,Scalar::all(-1),Scalar::all(-1),signed_mask);
				vector<int> params;
				params.push_back(CV_IMWRITE_JPEG_QUALITY);
				params.push_back(100);
				imwrite(string(current_img_path +"_match.jpg"), output_img,params);
			}
			if(correct_matches <= 4)
				continue;

			vector<Vec2d> corners;
			corners.push_back(Vec2d(0,0));
			corners.push_back(Vec2d(analyzed_img.rgb_image.cols-1,0));
			corners.push_back(Vec2d(0,analyzed_img.rgb_image.rows-1));
			corners.push_back(Vec2d(analyzed_img.rgb_image.cols-1,analyzed_img.rgb_image.rows-1));

			vector<Vec2d> trafo_corners(4);
			perspectiveTransform(corners, trafo_corners, H.inv());

			Vec2d min_vec, max_vec;
			getMinMaxVectors(trafo_corners,  min_vec, max_vec);

			Vec2i shift,padding;
			if(min_vec[1] < 0)
				shift[1] = ceil(fabs(min_vec[1]));
			else
				shift[1] = 0;

			if(min_vec[0] < 0)
				shift[0] = ceil(fabs(min_vec[0]));
			else
				shift[0] = 0;

			if(max_vec[1] >= base_img.bw_image.rows)
				padding[1] = ceil(max_vec[1]) -base_img.bw_image.rows -1;
			else
				padding[1]= 0;

			if(max_vec[0] >= base_img.bw_image.cols)
				padding[0] = ceil(max_vec[0]) -base_img.bw_image.cols -1;
			else
				padding[0]= 0;

			Size out_size(shift[0]+padding[0]+base_img.bw_image.cols,shift[1]+padding[1]+base_img.bw_image.rows);

			Mat out_img = Mat::zeros(out_size,CV_8UC3);

			Mat first_part = out_img(Range(shift[1],shift[1]+base_img.bw_image.rows),Range(shift[0],shift[0]+base_img.bw_image.cols));
			base_img.rgb_image.copyTo(first_part);


			Mat T = Mat::eye(Size(3,3),CV_64FC1);
			T.at<double>(0,2) = -shift[0];
			T.at<double>(1,2) = -shift[1];
			Mat shifted_H =  H * T ; // care opencv multiplies the wrong way!!!
			warpPerspective(analyzed_img.rgb_image, out_img, shifted_H.inv(), out_size, INTER_CUBIC,BORDER_TRANSPARENT);

			vector<int> params;
			params.push_back(CV_IMWRITE_JPEG_QUALITY);
			params.push_back(100);
			imwrite(string(current_img_path +"_stitched.jpg"), out_img,params);

		}
	}




}

void Panograph::stitchSmoothly(const cv::Mat & big_img_mask, const cv::Mat & big_pano_mask, const cv::Mat & overlap_mask, const cv::Mat &  big_warped_img, const cv::Mat &  shifted_pano, cv::Mat & out_img)
{
	Mat weight_img;
	calculateTransitionWeighting(big_img_mask,overlap_mask,weight_img);

	Vec3b zero_vec(0,0,0);
	for(int y = 0 ; y < overlap_mask.rows ; y++)
		for(int x = 0; x < overlap_mask.cols; x++)
		{

			if(big_img_mask.at<char>(y,x) == 1)
			{
				if(big_pano_mask.at<char>(y,x) == 1) //overlap
				{
					float weight = weight_img.at<float>(y,x);
					for(int c= 0; c < 3 ; c++)
					{
						out_img.at<cv::Vec3b>(y,x)[c] = (unsigned char)((((float)big_warped_img.at<cv::Vec3b>(y,x)[c])*(weight)+((float)shifted_pano.at<cv::Vec3b>(y,x)[c]*(1-weight))));
					}
				}
				else //only warped
				{
					out_img.at<cv::Vec3b>(y,x) = big_warped_img.at<cv::Vec3b>(y,x);
				}
			}
		}
}

void Panograph::stitchAveraging(const cv::Mat & big_img_mask, const cv::Mat & big_pano_mask, const cv::Mat & overlap_mask, const cv::Mat &  big_warped_img, const cv::Mat &  shifted_pano, cv::Mat & out_img)
{

	Vec3b zero_vec(0,0,0);
	for(int y = 0 ; y < overlap_mask.rows ; y++)
		for(int x = 0; x < overlap_mask.cols; x++)
		{

			if(big_img_mask.at<char>(y,x) == 1)
			{
				if(big_pano_mask.at<char>(y,x) == 1) //overlap
				{
					for(int c= 0; c < 3 ; c++)
					{
						out_img.at<cv::Vec3b>(y,x)[c] = (unsigned char)((((float)big_warped_img.at<cv::Vec3b>(y,x)[c])+((float)shifted_pano.at<cv::Vec3b>(y,x)[c]))/2);
					}
				}
				else //only warped
				{
					out_img.at<cv::Vec3b>(y,x) = big_warped_img.at<cv::Vec3b>(y,x);
				}
			}
		}
}

void Panograph::calculateTransitionWeighting(const Mat & big_img_mask, const Mat & overlap_mask, Mat  & weight_img)
{
	int rows = big_img_mask.rows;
	int cols = big_img_mask.cols;


	//first calculate centroids
	long img_sum[3] = {0,0,0};
	long overlap_sum[3] = {0,0,0};

	for(int y = 0 ; y < rows ; y++)
		for(int x = 0; x < cols; x++)
		{
			if(big_img_mask.at<char>(y,x) == 1)
			{
				img_sum[0] += x;
				img_sum[1] += y;
				img_sum[2]++;
			}
			if(overlap_mask.at<char>(y,x) == 1)
			{
				overlap_sum[0] += x;
				overlap_sum[1] += y;
				overlap_sum[2]++;
			}
		}

	int img_centroid[2] = {(int)(img_sum[0]/img_sum[2]),(int)(img_sum[1]/img_sum[2]) };
	int overlap_centroid[2] = {(int)(overlap_sum[0]/overlap_sum[2]),(int)(overlap_sum[1]/overlap_sum[2]) };

	//total overlap -> just average!
	if(img_centroid[0] == overlap_centroid[0]  && img_centroid[1] == overlap_centroid[1])
	{

		weight_img = Mat::zeros(rows,cols,CV_32FC1);
		for(int y = 0 ; y < rows ; y++)
			for(int x = 0; x < cols; x++)
			{
				if(overlap_mask.at<char>(y,x) == 1)
					weight_img.at<float>(y,x) = 0.5;
			}
		return;
	}

	//find pixel that is farthest away from img_centroid

	float centroid_vec[2] = {img_centroid[0]-overlap_centroid[0],img_centroid[1]-overlap_centroid[1]};
	float perpendicular_vec[2] = {-centroid_vec[1],centroid_vec[0]};

	Point pt1;
	Point pt2;
	if(perpendicular_vec[0] != 0)
	{
		float k = perpendicular_vec[1]/perpendicular_vec[0];
		//cout << "k: " << k << " perpendicular_vec[0]: " << perpendicular_vec[0] << " perpendicular_vec[1]: " << perpendicular_vec[1] << endl;
		float d = (float)img_centroid[1] - k * (float)img_centroid[0];
		pt1.x = 0;
		pt1.y = (int) d;
		pt2.x = cols-1;
		pt2.y = (int)(k* (float)pt2.x + d);
	}
	else // vertical line
	{
		pt1.x = img_centroid[0];
		pt1.y = 0;
		pt2.x = img_centroid[0];
		pt2.y = rows-1;

	}
	Mat line_img = Mat::ones(rows,cols,CV_8UC1);
	line(line_img, pt1,  pt2, 0);

	// calculate distance transform to line
	Mat distance_img;
	distanceTransform(line_img, distance_img,CV_DIST_L2,3);

	// get max in mask

	int farthest_pixel[2]= {-1, -1};
	float max_dist = 0;
	for(int y = 0 ; y < rows ; y++)
		for(int x = 0; x < cols; x++)
		{
			if(overlap_mask.at<char>(y,x) == 1)
			{
				if(distance_img.at<float>(y,x) > max_dist)
				{
					max_dist = distance_img.at<float>(y,x);
					farthest_pixel[0] = x;
					farthest_pixel[1] = y;

				}
			}
		}


	//draw line that goes through farthest pixel and is perpendicular to centroid vector

	//Point pt1;
	//Point pt2;
	if(perpendicular_vec[0] != 0)
	{
		float k = perpendicular_vec[1]/perpendicular_vec[0];
		//cout << "k: " << k << " perpendicular_vec[0]: " << perpendicular_vec[0] << " perpendicular_vec[1]: " << perpendicular_vec[1] << endl;
		float d = (float)farthest_pixel[1] - k * (float)farthest_pixel[0];
		pt1.x = 0;
		pt1.y = (int) d;
		pt2.x = cols-1;
		pt2.y = (int)(k* (float)pt2.x + d);
	}
	else // vertical line
	{
		pt1.x = farthest_pixel[0];
		pt1.y = 0;
		pt2.x = farthest_pixel[0];
		pt2.y = rows-1;

	}
	/*Mat */line_img = Mat::ones(rows,cols,CV_8UC1);
	line(line_img, pt1,  pt2, 0);

	//debug
	/*vector<int> params;
	params.push_back(CV_IMWRITE_JPEG_QUALITY);
	params.push_back(100);
	imwrite(string("images/line.jpg"), line_img*255,params);*/


	// calculate distance transform to line
	//Mat distance_img;
	distanceTransform(line_img, distance_img,CV_DIST_L2,3);

	// get max in mask
	float max_distance = 0;
	for(int y = 0 ; y < rows ; y++)
		for(int x = 0; x < cols; x++)
		{
			if(overlap_mask.at<char>(y,x) == 1)
			{
				if(distance_img.at<float>(y,x) > max_distance)
				{
					max_distance = distance_img.at<float>(y,x);

				}
			}
			else
			{
				distance_img.at<float>(y,x) = 0;
			}
		}
	weight_img = distance_img * (1/max_distance);
}



void Panograph::shiftKeyPoints(vector<KeyPoint> & kps, Vec2i shift)
{
	for(int i = 0; i < (int)kps.size() ; i++)
	{
		kps.at(i).pt.x += shift[0];
		kps.at(i).pt.y += shift[1];
	}
}

void Panograph::warpKeyPoints(vector<KeyPoint> & input_kps, vector<KeyPoint> & output_kps, Mat & H)
{
	output_kps = vector<KeyPoint>(input_kps.size());
	vector<Vec2f> input_coord_vec(input_kps.size());
	vector<Vec2f> output_coord_vec(input_kps.size());
	for(int i = 0; i < (int)input_kps.size() ; i++)
	{
		Vec2f kp2d;
		kp2d[0] = input_kps.at(i).pt.x;
		kp2d[1] = input_kps.at(i).pt.y;
		input_coord_vec[i] = kp2d;
	}

	perspectiveTransform(input_coord_vec, output_coord_vec, H.inv());

	for(int i = 0; i < (int)input_kps.size() ; i++)
	{
		output_kps[i] = input_kps[i];
		output_kps[i].pt.x = output_coord_vec[i][0];
		output_kps[i].pt.y = output_coord_vec[i][1];
	}

}

void Panograph::getMinMaxVectors(vector< Vec2d > &corners, Vec2d & min_vec, Vec2d & max_vec)
{
	min_vec = Vec2d(__DBL_MAX__,__DBL_MAX__);
	max_vec = Vec2d(__DBL_MIN__,__DBL_MIN__);
	for(int i = 0; i < (int)corners.size(); i++)
	{
		Vec2d corner = corners.at(i);
		if(corner[0] < min_vec[0])
			min_vec[0] = corner[0];
		if(corner[1] < min_vec[1])
			min_vec[1] = corner[1];
		if(corner[0] > max_vec[0])
			max_vec[0] = corner[0];
		if(corner[1] > max_vec[1])
			max_vec[1] = corner[1];
	}
}

void Panograph::matches2points(const std::vector<cv::KeyPoint>& train, const std::vector<cv::KeyPoint>& query,
		const std::vector<cv::DMatch>& matches, std::vector<cv::Point2f>& pts_train,
		std::vector<Point2f>& pts_query)
{

	pts_train.clear();
	pts_query.clear();
	pts_train.reserve(matches.size());
	pts_query.reserve(matches.size());

	size_t i = 0;

	for (; i < matches.size(); i++)
	{

		const DMatch & dmatch = matches[i];

		pts_query.push_back(query[dmatch.queryIdx].pt);
		pts_train.push_back(train[dmatch.trainIdx].pt);

	}

}



string Panograph::int2string(int number)
{
	stringstream ss;
	ss << number;
	return ss.str();
}
